//////////////////////////////////////////////////////////////////////
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
//  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  Register.cpp
//
//          Server registration code.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <windows.h>
#include <ole2.h>
#include "msctf.h"
#include "globals.h"


//+---------------------------------------------------------------------------
//
//  RegisterCategories
//
//----------------------------------------------------------------------------

BOOL RegisterCategories()
{
    ITfCategoryMgr *pCategoryMgr;
    HRESULT hr;

    hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, 
                          IID_ITfCategoryMgr, (void**)&pCategoryMgr);

    if (hr != S_OK)
	{
		DebugPrintfW(_TEXT("Failed create instance CLSID_TF_CategoryMgr. hr=%0x"),hr);
        return FALSE;
	}

    hr = pCategoryMgr->RegisterCategory(c_clsidTextService,
                                        GUID_TFCAT_TIP_KEYBOARD, 
                                        c_clsidTextService);

	//
    // register this text service to GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER category.
    //
    hr = pCategoryMgr->RegisterCategory(c_clsidTextService,
                                        GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, 
                                        c_clsidTextService);
 

    pCategoryMgr->Release();
    return (hr == S_OK);
}

//+---------------------------------------------------------------------------
//
//  RegisterProfiles
//
//----------------------------------------------------------------------------

HRESULT RegisterProfilesWithProfileManager()
{
    ITfInputProcessorProfiles *pInputProcessProfiles=NULL;
    WCHAR achIconFile[MAX_PATH];
    char achFileNameA[MAX_PATH];
    DWORD cchA;
    int cchIconFile;
    HRESULT hr;

    hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER,
                          IID_ITfInputProcessorProfiles, (void**)&pInputProcessProfiles);

    if (FAILED(hr))
	{
		DebugPrintfW(_TEXT("Failed create instance CLSID_TF_InputProcessorProfiles. hr=%0x"),hr);
        return hr;
	}

	ITfInputProcessorProfileMgr *pProfileMgr=NULL;

	hr = pInputProcessProfiles->QueryInterface(__uuidof(ITfInputProcessorProfileMgr), (LPVOID*)&pProfileMgr);
	if (FAILED(hr))
    {
		DebugPrintfW(_TEXT("Failed query interface for ITfInputProcessorProfileMgr. hr=%0x"),hr);
		goto Exit;
	}


    cchA = GetModuleFileNameA(g_hInst, achFileNameA, IME_ARRAYSIZE(achFileNameA));

    cchIconFile = MultiByteToWideChar(CP_ACP, 0, achFileNameA, cchA, achIconFile, IME_ARRAYSIZE(achIconFile)-1);
    achIconFile[cchIconFile] = '\0';

	hr=pProfileMgr->RegisterProfile(c_clsidTextService,
		TEXTSERVICE_LANGID,
		c_guidProfile, 
		TEXTSERVICE_DESC, 
        (ULONG)_tcslen(TEXTSERVICE_DESC),
		achIconFile,
        cchIconFile,
        TEXTSERVICE_ICON_INDEX, NULL, 0, TRUE, 0);

	if(FAILED(hr))
	{
		DebugPrintfW(_TEXT("Failed to call ITfInputProcessorProfileMgr:RegisterProfile. hr=%0x"),hr);
	}
Exit:
	if(pProfileMgr!=NULL)
		pProfileMgr->Release();
	if(pInputProcessProfiles!=NULL)
		pInputProcessProfiles->Release();
    return hr;
}


//+---------------------------------------------------------------------------
//
//  RegisterProfiles
//
//----------------------------------------------------------------------------

/*BOOL RegisterProfiles()
{
    ITfInputProcessorProfiles *pInputProcessProfiles;
    WCHAR achIconFile[MAX_PATH];
    char achFileNameA[MAX_PATH];
    DWORD cchA;
    int cchIconFile;
    HRESULT hr;

    hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER,
                          IID_ITfInputProcessorProfiles, (void**)&pInputProcessProfiles);

    if (hr != S_OK)
	{
		DebugPrintfW(_TEXT("Failed create instance CLSID_TF_InputProcessorProfiles. hr=%0x"),hr);
        return E_FAIL;
	}

    hr = pInputProcessProfiles->Register(c_clsidTextService);

    if (hr != S_OK)
        goto Exit;

    cchA = GetModuleFileNameA(g_hInst, achFileNameA, IME_ARRAYSIZE(achFileNameA));

    cchIconFile = MultiByteToWideChar(CP_ACP, 0, achFileNameA, cchA, achIconFile, IME_ARRAYSIZE(achIconFile)-1);
    achIconFile[cchIconFile] = '\0';

    hr = pInputProcessProfiles->AddLanguageProfile(c_clsidTextService,
                                  TEXTSERVICE_LANGID, 
                                  c_guidProfile, 
                                  TEXTSERVICE_DESC, 
                                  (ULONG)_tcslen(TEXTSERVICE_DESC),
                                  achIconFile,
                                  cchIconFile,
                                  TEXTSERVICE_ICON_INDEX);

Exit:
    pInputProcessProfiles->Release();
    return (hr == S_OK);
}*/

//+---------------------------------------------------------------------------
//
//  UnregisterCategories
//
//----------------------------------------------------------------------------
void UnregisterCategories()
{
    ITfCategoryMgr *pCategoryMgr;
    HRESULT hr;

    hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, 
                          IID_ITfCategoryMgr, (void**)&pCategoryMgr);

    if (hr != S_OK)
	{
		DebugPrintfW(_TEXT("Failed create instance CLSID_TF_CategoryMgr. hr=%0x"),hr);
        return;
	}

    pCategoryMgr->UnregisterCategory(c_clsidTextService,
                                     GUID_TFCAT_TIP_KEYBOARD, 
                                     c_clsidTextService);

    pCategoryMgr->Release();
    return;
}
//+---------------------------------------------------------------------------
//
//  UnregisterProfiles
//
//----------------------------------------------------------------------------

void UnregisterProfiles()
{
    ITfInputProcessorProfiles *pInputProcessProfiles;
    HRESULT hr;

    hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER,
                          IID_ITfInputProcessorProfiles, (void**)&pInputProcessProfiles);

    if (FAILED(hr))
	{
		DebugPrintfW(_TEXT("Failed create instance CLSID_TF_InputProcessorProfiles. hr=%0x"),hr);
        return;
	}

	ITfInputProcessorProfileMgr *pProfileMgr=NULL;

	hr = pInputProcessProfiles->QueryInterface(__uuidof(ITfInputProcessorProfileMgr), (LPVOID*)&pProfileMgr);
	if (FAILED(hr))
    {
		DebugPrintfW(_TEXT("Failed query interface for ITfInputProcessorProfileMgr. hr=%0x"),hr);
		goto Exit;
	}

    pProfileMgr->UnregisterProfile(c_clsidTextService,TEXTSERVICE_LANGID,c_guidProfile,TF_URP_ALLPROFILES);
Exit:
	if(pProfileMgr!=NULL)
		pProfileMgr->Release();
	if(pInputProcessProfiles!=NULL)
		pInputProcessProfiles->Release();
}

//+---------------------------------------------------------------------------
//
// CLSIDToStringA
//
//----------------------------------------------------------------------------

/*BOOL CLSIDToStringA(REFGUID refGUID, char *pchA)
{
    static const BYTE GuidMap[] = {3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
                                   8, 9, '-', 10, 11, 12, 13, 14, 15};

    static const char szDigits[] = "0123456789ABCDEF";

    int i;
    char *p = pchA;

    const BYTE * pBytes = (const BYTE *) &refGUID;

    *p++ = '{';
    for (i = 0; i < sizeof(GuidMap); i++)
    {
        if (GuidMap[i] == '-')
        {
            *p++ = '-';
        }
        else
        {
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0xF0) >> 4 ];
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0x0F) ];
        }
    }

    *p++ = '}';
    *p   = '\0';

    return TRUE;
}*/

//+---------------------------------------------------------------------------
//
// RecurseDeleteKey
//
// RecurseDeleteKey is necessary because on NT RegDeleteKey doesn't work if the
// specified key has subkeys
//----------------------------------------------------------------------------
LONG RecurseDeleteKey(HKEY hParentKey, LPCTSTR lpszKey)
{
    HKEY hKey;
    LONG lRes;
    FILETIME time;
    TCHAR szBuffer[256];
    DWORD dwSize = IME_ARRAYSIZE(szBuffer);

    if (RegOpenKey(hParentKey, lpszKey, &hKey) != ERROR_SUCCESS)
        return ERROR_SUCCESS; // let's assume it couldn't be opened because it's not there

    lRes = ERROR_SUCCESS;
    while (RegEnumKeyEx(hKey, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time)==ERROR_SUCCESS)
    {
        szBuffer[IME_ARRAYSIZE(szBuffer)-1] = '\0';
        lRes = RecurseDeleteKey(hKey, szBuffer);
        if (lRes != ERROR_SUCCESS)
            break;
        dwSize = IME_ARRAYSIZE(szBuffer);
    }
    RegCloseKey(hKey);

    return lRes == ERROR_SUCCESS ? RegDeleteKey(hParentKey, lpszKey) : lRes;
}

//+---------------------------------------------------------------------------
//
//  RegisterServer
//
//----------------------------------------------------------------------------
//GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER 
BOOL RegisterServer()
{
    DWORD dw;
    HKEY hKey;
    HKEY hSubKey;
    BOOL fRet;
   // TCHAR achIMEKey[IME_ARRAYSIZE(c_szInfoKeyPrefix) + CLSID_STRLEN];
    TCHAR achFileName[MAX_PATH];

    /*if (!CLSIDToStringA(c_clsidTextService, achIMEKey + IME_ARRAYSIZE(c_szInfoKeyPrefix) - 1))
        return FALSE;
	memcpy(achIMEKey, c_szInfoKeyPrefix, sizeof(c_szInfoKeyPrefix)-sizeof(TCHAR));*/
	LPTSTR achIMEKey=NULL;
	HRESULT hr=GetFullCLSIDPath(&achIMEKey);

	if(FAILED(hr))
	{
		DebugPrintfW(_T("failed to call GetFullCLSIDPath. hr=%X \n"), hr);
		return FALSE;
	}

    if (fRet = RegCreateKeyEx(HKEY_CLASSES_ROOT, achIMEKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dw)
            == ERROR_SUCCESS)
    {
        fRet &= RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)TEXTSERVICE_DESC, (lstrlen(TEXTSERVICE_DESC)+1)*sizeof(TCHAR))
            == ERROR_SUCCESS;

        if (fRet &= RegCreateKeyEx(hKey, c_szInProcSvr32, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hSubKey, &dw)
            == ERROR_SUCCESS)
        {
            dw = GetModuleFileName(g_hInst, achFileName, IME_ARRAYSIZE(achFileName));

            fRet &= RegSetValueEx(hSubKey, NULL, 0, REG_SZ, (BYTE *)achFileName, (lstrlen(achFileName)+1)*sizeof(TCHAR)) == ERROR_SUCCESS;
            fRet &= RegSetValueEx(hSubKey, c_szModelName, 0, REG_SZ, (BYTE *)TEXTSERVICE_MODEL, (lstrlen(TEXTSERVICE_MODEL)+1)*sizeof(TCHAR)) == ERROR_SUCCESS;
            RegCloseKey(hSubKey);
        }
		else
		{
			DWORD dwError=::GetLastError();
			HandleWin32Error(dwError);
		}
        RegCloseKey(hKey);
    }
	else
	{
		DWORD dwError=::GetLastError();
		HandleWin32Error(dwError);
	}
	::CoTaskMemFree(achIMEKey);
    return fRet;
}

//+---------------------------------------------------------------------------
//
//  UnregisterServer
//
//----------------------------------------------------------------------------

void UnregisterServer()
{
	LPTSTR clsidPath=NULL;
	HRESULT hr=GetFullCLSIDPath(&clsidPath);

	if(FAILED(hr))
	{
		DebugPrintfW(_T("failed to call GetFullCLSIDPath. hr=%X \n"), hr);
		return;
	}
    RecurseDeleteKey(HKEY_CLASSES_ROOT, clsidPath);

	::CoTaskMemFree((LPVOID)clsidPath);

}

HRESULT GetFullCLSIDPath(LPTSTR* clsidPath)
{
	HRESULT hr=S_OK;
	TCHAR achIMEKey[IME_ARRAYSIZE(c_szInfoKeyPrefix) + CLSID_STRLEN];
	
	LPOLESTR lpstrCLSIDTextService=NULL;
	hr=StringFromCLSID(c_clsidTextService,&lpstrCLSIDTextService);
	if(FAILED(hr) || lpstrCLSIDTextService==NULL )
	{
		DebugPrintfW(_T("failed to call StringFromCLSID. hr=%X \n"), hr);
		return hr;
	}
	
	COLE2T tClsidTextService(lpstrCLSIDTextService);
	
	hr=StringCbCopy(achIMEKey,(sizeof(c_szInfoKeyPrefix)+1)*sizeof(TCHAR), c_szInfoKeyPrefix); 
	// to do
	if(FAILED(hr))
	{
		DebugPrintfW(_T("StringCbCopy failed when copying c_szInfoKeyPrefix. hr=%X  \n"),hr);
		goto CLEANUP;
	}

	hr=StringCbCopy(achIMEKey+_tcslen(c_szInfoKeyPrefix),(CLSID_STRLEN+1)*sizeof(TCHAR),tClsidTextService);
	if(FAILED(hr))
	{
		DebugPrintfW(_T("StringCbCopy failed when copying tClsidTextService. hr=%X  \n"),hr);
		goto CLEANUP;
	}

	LPVOID buffer=::CoTaskMemAlloc((lstrlen(achIMEKey)+1)*sizeof(TCHAR));
	memcpy_s(buffer,(lstrlen(achIMEKey)+1)*sizeof(TCHAR), achIMEKey, (lstrlen(achIMEKey)+1)*sizeof(TCHAR));

	*clsidPath=(LPTSTR)buffer;

CLEANUP:
	::CoTaskMemFree(lpstrCLSIDTextService);
	return hr;
}

void HandleWin32Error(DWORD dwError)
{
	// Throw the error
		CError::DispatchError(HRESULT_FROM_WIN32(dwError),	// This represents the error
											c_clsidTextService,	// This is the GUID of component throwing error
											_T("failed"),	// This is generally displayed as the title
											NULL,	// This is the description
											0,			// This is the context in the help file
											NULL);	
		DebugPrintfW(_T("failed to call RegCreateKeyEx. dwError=%d \n"),dwError);
}